package com.yanglei.demo.client;

import com.yanglei.demo.client.dto.CommonResult;
import com.yanglei.demo.client.dto.oauth2.OAuth2AccessTokenRespDTO;
import com.yanglei.demo.client.dto.oauth2.OAuth2CheckTokenRespDTO;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;

/**
 * @ClassName OAuth2Client
 * @Description TODO
 * @Author hljstart
 * @Date 2025/11/3 22:24
 * @Version 1.0
 */
@Component
public class OAuth2Client {

    @Resource
    private RestTemplate restTemplate;

    private static final String BASE_URL = "http://127.0.0.1:48080/admin-api/system/oauth2";

    /**
     * 租户编号，默认使用1
     */
    private static final Long TENANT_ID = 1L;

    private static final String CLIENT_ID = "yudao-sso-demo-by-code1";

    private static final String CLIENT_SECRET = "test";

    /**
     * 获取访问token和刷新token
     *
     * @param code
     * @param redirectUri
     * @return
     */
    public CommonResult<OAuth2AccessTokenRespDTO> postAccessToken(String code, String redirectUri) {
        // 1、构建请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        // 设置租户信息
        headers.set("tenant-id", TENANT_ID.toString());

        // 这里要将 CLIENT_ID 和 CLIENT_SECRET 设置成Authorization 进行了加密
        addClientHeader(headers);

        // 构建请求参数
        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
        // grant_type 必选项，必须为authorization_code
        body.add("grant_type", "authorization_code");
        body.add("code", code);
        body.add("redirect_uri", redirectUri);

        ResponseEntity<CommonResult<OAuth2AccessTokenRespDTO>> exchange = restTemplate.exchange(
                BASE_URL + "/token",
                HttpMethod.POST,
                new HttpEntity<>(body, headers),
                new ParameterizedTypeReference<CommonResult<OAuth2AccessTokenRespDTO>>() {
                });
        Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "相应必须是200成功");
        return exchange.getBody();
    }


    private static void addClientHeader(HttpHeaders headers) {
        String client = CLIENT_ID + ":" + CLIENT_SECRET;
        client = Base64Utils.encodeToString(client.getBytes(StandardCharsets.UTF_8));
        headers.add("Authorization", "Basic " + client);
    }

    /**
     * 校验访问令牌
     *
     * @param token
     * @return
     */
    public CommonResult<OAuth2CheckTokenRespDTO> checkToken(String token) {
        // 1、创建请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        // 设置租户id
        headers.set("tenant-id", TENANT_ID.toString());

        // 设置客户端id和客户端密钥
        addClientHeader(headers);

        // 构建请求参数
        LinkedMultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
        body.add("token", token);

        ResponseEntity<CommonResult<OAuth2CheckTokenRespDTO>> exchange = restTemplate.exchange(
                BASE_URL + "/check-token",
                HttpMethod.POST,
                new HttpEntity<>(body, headers),
                new ParameterizedTypeReference<CommonResult<OAuth2CheckTokenRespDTO>>() {
                });
        Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "相应必须是 200 成功");
        return exchange.getBody();
    }

    public CommonResult<Boolean> revokeToken(String token) {
        // 1、创建请求参数
        HttpHeaders headers = new HttpHeaders();
        // 主要用于提交HTML表单数据，在Spring的RestTemplate模拟表单提交的时候，需要显示设置请求头，否则服务器可能无法正确解析请求头
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.set("tenant-id", TENANT_ID.toString());
        /*
         * 在spring 框架中是一个特殊设计的Map,用于处理HTTP请求和响应中的参数，他支持一个key对应多个value
         * 为什么表单提交需要他呢？
         *  1、支持多值参数
         *  2、自动处理编码，在RestTemplate中会自动转换成key=value&key2=value2格式，会对字符URL进行编码（空格-> + 中文->%xx）
         *  3、可以保证参数的顺序
         */
        // 设置请求头
        addClientHeader(headers);
        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
        body.add("token", token);

        ResponseEntity<CommonResult<Boolean>> exchange = restTemplate.exchange(
                BASE_URL + "/token",
                HttpMethod.DELETE,
                new HttpEntity<>(body, headers),
                new ParameterizedTypeReference<CommonResult<Boolean>>() {
                }
        );
        Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应码必须为 200 成功");

        return exchange.getBody();
    }

    public CommonResult<OAuth2AccessTokenRespDTO> refreshToken(String refreshToken) {
        // 1、准备请求头信息
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.set("tenant-id", TENANT_ID.toString());
        addClientHeader(headers);
        MultiValueMap<String,Object> body = new LinkedMultiValueMap<String,Object>();

        body.add("grant_type","refresh_token");
        body.add("refresh_token",refreshToken);

        ResponseEntity<CommonResult<OAuth2AccessTokenRespDTO>> exchange = restTemplate.exchange(
                BASE_URL + "/token",
                HttpMethod.POST,
                new HttpEntity<>(body, headers),
                new ParameterizedTypeReference<CommonResult<OAuth2AccessTokenRespDTO>>() {
                }
        );
        return exchange.getBody();
    }
}
